<template>
  <el-dialog
    v-model="isDialogVisible"
    :width="config.width"
    :title="dialogTitle"
    @closed="handleDialogClosed"
    destroy-on-close
  >
    <el-form ref="dialogFormRef" :model="dialogForm" :label-width="config.labelWidth" :rules="rules">
      <el-row :gutter="row.gutter" v-for="(row, index) in config.layout" :key="index">
        <el-col :span="row.colSpan ?? 24 / row.formItems.length" v-for="item in row.formItems" :key="item.prop">
          <el-form-item :label="config.showLabel ? item.label : ''" :prop="item.prop">
            <template v-if="item.type === 'input'">
              <el-input v-model="dialogForm[item.prop]" v-bind="item.bind" :placeholder="item.placeholder" clearable />
            </template>
            <template v-else-if="item.type === 'select'">
              <el-select
                class="m-full-width"
                v-model="dialogForm[item.prop]"
                v-bind="item.bind"
                :placeholder="item.placeholder"
                clearable
              >
                <el-option
                  :label="option.label"
                  :value="option.value"
                  :disabled="option.disabled"
                  v-for="option in item.optionsGetter && item.optionsGetter.length
                    ? $store.getters[item.optionsGetter]
                    : item.options"
                  :key="option.label"
                >
                  <slot :name="item.optionSlot" :option="option"></slot>
                </el-option>
              </el-select>
            </template>
            <template v-else-if="item.type === 'number'">
              <el-input-number
                class="m-full-width"
                v-model="dialogForm[item.prop]"
                v-bind="item.bind"
              ></el-input-number>
            </template>
            <template v-else-if="item.type === 'radio'">
              <el-radio-group v-model="dialogForm[item.prop]" size="medium">
                <el-radio
                  v-bind="item.bind"
                  :label="option.value"
                  v-for="option in item.optionsGetter && item.optionsGetter.length
                    ? $store.getters[item.optionsGetter]
                    : item.options"
                  :key="option.label"
                >
                  {{ option.label }}
                </el-radio>
              </el-radio-group>
            </template>
            <template v-else-if="item.type === 'textarea'">
              <el-input
                type="textarea"
                v-model="dialogForm[item.prop]"
                v-bind="item.bind"
                :placeholder="item.placeholder"
                clearable
              />
            </template>
            <template v-else-if="item.type === 'switch'">
              <el-switch v-model="dialogForm[item.prop]" v-bind="item.bind"></el-switch>
            </template>
            <template v-else-if="item.type === 'cascader'">
              <el-cascader
                class="m-full-width"
                v-model="dialogForm[item.prop]"
                v-bind="item.bind"
                :options="
                  item.optionsGetter && item.optionsGetter.length ? $store.getters[item.optionsGetter] : item.options
                "
                :placeholder="item.placeholder"
                clearable
              />
            </template>
          </el-form-item>
        </el-col>
      </el-row>
    </el-form>
    <template #footer>
      <slot name="footer"></slot>
    </template>
  </el-dialog>
</template>

<script lang="ts">
import { computed, defineComponent, nextTick, PropType, ref } from 'vue'
import { ElForm } from 'element-plus'
import { Callback } from 'element-plus/lib/components/form/src/form.vue'
import { DialogConfig } from './type'
import { copy } from '@/utils/common'

export default defineComponent({
  name: 'MmDialog',
  props: {
    title: {
      type: String,
      default: ''
    },
    config: {
      type: Object as PropType<DialogConfig>,
      required: true
    },
    defaultData: {
      type: Object,
      default: () => ({})
    },
    rules: {
      type: Object,
      default: () => ({})
    }
  },
  setup(props) {
    const isDialogVisible = ref(false)
    const dialogFormRawData: any = {}
    props.config.layout.forEach((layout) => {
      layout.formItems.forEach((item) => {
        dialogFormRawData[item.prop] = item.default ?? ''
      })
    })
    const dialogTitle = computed(() =>
      props.title !== '' ? props.title : Object.keys(props.defaultData).length ? '编辑' : '添加'
    )
    const dialogForm = ref(dialogFormRawData)
    const dialogFormRef = ref<InstanceType<typeof ElForm>>()

    // 显示或隐藏对话框
    const visible = (isVisible = false) => {
      isDialogVisible.value = isVisible

      nextTick(() => {
        if (Object.keys(props.defaultData).length) {
          copy(dialogForm.value, props.defaultData)
        }
      })
    }

    // 表单验证
    const validate = (callback?: Callback) => {
      return dialogFormRef.value?.validate(callback)
    }

    // 关闭事件
    const handleDialogClosed = () => {
      dialogFormRef.value?.resetFields()
    }

    return { isDialogVisible, dialogTitle, dialogForm, dialogFormRef, visible, validate, handleDialogClosed }
  }
})
</script>

<style lang="less" scoped>
// 使用 el-radio-group 后 form-item 会高几个像素，原因未知
.el-radio-group {
  font-size: unset;
}
</style>
